﻿// SKNotes - a note taking and gathering tool
// Copyright (C) 2009  Stefan Kueng
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.ComponentModel;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Navigation;
using IfacesEnumsStructsClasses;
using SKNotes.Utilities;
using SKNotes.Utilities.Events;

namespace SKNotes.CustomControls.HtmlEditor.Control
{
    /// <summary>
    /// Interaction logic for UserControl1.xaml
    /// </summary>
    public partial class Editor
        : UserControl
        , IOleClientSite
        , IDocHostShowUI
        , IDocHostUIHandler
        , DWebBrowserEvents2
        , IfacesEnumsStructsClasses.IDropTarget
        , IfacesEnumsStructsClasses.IServiceProvider
        , IOleCommandTarget
        , IProtectFocus
        , IHTMLEventCallBack
    {
        public static readonly RoutedEvent HtmlChangedEvent;
        public static readonly DependencyProperty IsDocumentLoadedProperty;
        public static readonly DependencyPropertyKey IsDocumentLoadedPropertyKey;

        public static new readonly RoutedEvent DragEnterEvent;
        public static new readonly RoutedEvent DragOverEvent;
        public static new readonly RoutedEvent DragLeaveEvent;
        public static new readonly RoutedEvent DropEvent;
        public static readonly RoutedEvent BeforePasteEvent;
        public static readonly RoutedEvent BeforeNavigate2Event;

        private cHTMLDocumentEvents TopLevelHtmlDocumentevents = new cHTMLDocumentEvents();
        private cHTMLWindowEvents TopLevelHtmlWindowEvents = new cHTMLWindowEvents();

        private const string LOCATION_DEFAULT = "about:blank";
        private IWebBrowser2 IWebBrowser2Interface = null;
        private IOleObject IOleObjectInterface = null;
        private IOleCommandTarget IOleCommandTargetInterface = null;
        private IConnectionPoint IConnectionPointInterface = null;
        private IfacesEnumsStructsClasses.IDropTarget OriginalDropTarget = null;
        private int _contentHashCode;
        private string _contentToLoad;
        private object _syncRoot = new object();
        private int dwCookie = 0;

        [Browsable(true), Description("Occurs when the html is changed.")]
        public event RoutedEventHandler HtmlChanged
        {
            add { AddHandler(HtmlChangedEvent, value); }
            remove { RemoveHandler(HtmlChangedEvent, value); }
        }

        [Browsable(true), Description("Occurs when an object is dragged into the editor.")]
        public new event RoutedEventHandler DragEnter
        {
            add { AddHandler(DragEnterEvent, value); }
            remove { RemoveHandler(DragEnterEvent, value); }
        }
        [Browsable(true), Description("Occurs when an object is dragged over the editor.")]
        public new event RoutedEventHandler DragOver
        {
            add { AddHandler(DragOverEvent, value); }
            remove { RemoveHandler(DragOverEvent, value); }
        }
        [Browsable(true), Description("Occurs when an object is dragged outside the editor.")]
        public new event RoutedEventHandler DragLeave
        {
            add { AddHandler(DragLeaveEvent, value); }
            remove { RemoveHandler(DragLeaveEvent, value); }
        }
        [Browsable(true), Description("Occurs when an object is dropped on the editor.")]
        public new event RoutedEventHandler Drop
        {
            add { AddHandler(DropEvent, value); }
            remove { RemoveHandler(DropEvent, value); }
        }
        [Browsable(true), Description("Occurs before a paste happens.")]
        public event RoutedEventHandler BeforePaste
        {
            add { AddHandler(BeforePasteEvent, value); }
            remove { RemoveHandler(BeforePasteEvent, value); }
        }
        [Browsable(true), Description("Occurs before the control navigates to a new url.")]
        public event RoutedEventHandler BeforeNavigate2
        {
            add { AddHandler(BeforeNavigate2Event, value); }
            remove { RemoveHandler(BeforeNavigate2Event, value); }
        }

        /// <summary>
        /// Gets a value indicating if a document is currently loaded.
        /// </summary>
        public bool IsDocumentLoaded
        {
            get { return (bool)this.GetValue(IsDocumentLoadedProperty); }
            private set { this.SetValue(IsDocumentLoadedPropertyKey, value); }
        }

        public bool IsHTMLChanged
        {
            get 
            {
                int hashCode = GetText(true).GetHashCode();
                return (hashCode != this._contentHashCode);
            }

            set 
            {
                // setting this property always resets it, no
                // mater what value it is assigned!
                this._contentHashCode = GetText(true).GetHashCode();
            }
        }

        static Editor()
        {
            HtmlChangedEvent = EventManager.RegisterRoutedEvent("HtmlChanged", RoutingStrategy.Bubble,
                typeof(RoutedEventHandler), typeof(Editor));

            DragEnterEvent = EventManager.RegisterRoutedEvent("DragEnter", RoutingStrategy.Bubble,
                typeof(DragEnterEventHandler), typeof(Editor));
            DragOverEvent = EventManager.RegisterRoutedEvent("DragOver", RoutingStrategy.Bubble,
                typeof(DragOverEventHandler), typeof(Editor));
            DragLeaveEvent = EventManager.RegisterRoutedEvent("DragLeave", RoutingStrategy.Bubble,
                typeof(RoutedEventHandler), typeof(Editor));
            DropEvent = EventManager.RegisterRoutedEvent("Drop", RoutingStrategy.Bubble,
                typeof(DropEventHandler), typeof(Editor));
            BeforePasteEvent = EventManager.RegisterRoutedEvent("BeforePaste", RoutingStrategy.Bubble,
                typeof(RoutedEventHandler), typeof(Editor));
            BeforeNavigate2Event = EventManager.RegisterRoutedEvent("BeforeNavigate2", RoutingStrategy.Bubble,
                typeof(BeforeNavigate2EventHandler), typeof(Editor));


            IsDocumentLoadedPropertyKey = DependencyProperty.RegisterReadOnly("IsDocumentLoaded", typeof(bool),
                typeof(NoteViewer), new PropertyMetadata(false));
            IsDocumentLoadedProperty = IsDocumentLoadedPropertyKey.DependencyProperty;
        }


        public Editor()
        {
            InitializeComponent();

            int[] dispIDs = { (int)HTMLEventDispIds.ID_ONMOUSEOVER, (int)HTMLEventDispIds.ID_ONMOUSEOUT, (int)HTMLEventDispIds.ID_ONCLICK, (int)HTMLEventDispIds.ID_ONDBLCLICK };
            TopLevelHtmlDocumentevents.InitHTMLEvents(this, dispIDs, Iid_Clsids.DIID_HTMLDocumentEvents2);

            if (!System.ComponentModel.DesignerProperties.GetIsInDesignMode(this))
            {
                this.Navigate();
            }
        }

        /// <summary>
        /// Loads the default document.
        /// </summary>
        /// <param name="uri"></param>
        public void Navigate()
        {
            this.LoadDocument(new Uri(LOCATION_DEFAULT));
        }

        /// <summary>
        /// Loads the document at the specified uri.
        /// </summary>
        /// <param name="uri"></param>
        public void Navigate(Uri uri)
        {
            if (uri == null)
            {
                this.LoadDocument(new Uri(LOCATION_DEFAULT));
            }
            else
            {
                this.LoadDocument(uri);
            }
        }

        /// <summary>
        /// Loads the specified html.
        /// </summary>
        /// <param name="uri"></param>
        public void Navigate(string html)
        {
            if (string.IsNullOrEmpty(html))
            {
                this.LoadDocument(new Uri(LOCATION_DEFAULT));
            }
            else
            {
                this.LoadDocument(html);
            }
        }

        /// <summary>
        /// Loads the html document contained in the specified stream.
        /// </summary>
        /// <param name="uri"></param>
        public void Navigate(Stream source)
        {
            if (source == null)
            {
                this.LoadDocument(new Uri(LOCATION_DEFAULT));
            }
            else
            {
                this.LoadDocument(source);
            }
        }

        protected override void OnPreviewKeyUp(KeyEventArgs e)
        {
            base.OnPreviewKeyUp(e);

            if (this.IsDocumentLoaded && this.mshtml.Document != null)
            {
                int hashCode = GetText(true).GetHashCode();
                if (hashCode != this._contentHashCode)
                {
                    this._contentHashCode = hashCode;
                    this.OnHtmlChanged();
                }
            }
        }

        /// <summary>
        /// Raises the <see cref="HtmlChanged"/> event.
        /// </summary>
        protected virtual void OnHtmlChanged()
        {
            RoutedEventArgs args = new RoutedEventArgs(HtmlChangedEvent);
            this.RaiseEvent(args);
        }

        /// <summary>
        /// Loads an html document from the specified source.
        /// </summary>
        /// <param name="source"></param>
        protected void LoadDocument(object source)
        {
            this._contentToLoad = null;
            this.IsDocumentLoaded = false;

            // to avoid getting a confirmation dialog "do you want to save your changes",
            // we just set the dirty flag to false and so mark the document as properly 'saved'
            OleCommandExec((int)MSHTML_COMMAND_IDS.IDM_SETDIRTY, false);

            this.mshtml.LoadCompleted -= new LoadCompletedEventHandler(webBrowser_LoadCompleted);
            this.mshtml.LoadCompleted += new LoadCompletedEventHandler(webBrowser_LoadCompleted);

            if (source is Uri)
            {
                //navigate to the a location by uri
                this.mshtml.Navigate((Uri)source);
                if (this.mshtml.Document != null)
                {
                    this._contentHashCode = GetText(true).GetHashCode();
                }
            }
            else
            {
                //navigate to an html document stream
                if (source is Stream)
                {
                    this.mshtml.NavigateToStream((Stream)source);
                    using (StreamReader reader = new StreamReader((Stream)source))
                    {
                        this._contentToLoad = reader.ReadToEnd();
                    }
                }
                else
                {
                    this._contentToLoad = source.ToString();
                }

                //navigate to html content
                this._contentHashCode = this._contentToLoad.GetHashCode();
                this.mshtml.NavigateToString(this._contentToLoad);
            }


            IWebBrowser2Interface = AccessInternals.GetMembers(this.mshtml, "_axIWebBrowser2") as IWebBrowser2;
            IOleObjectInterface = (IOleObject)IWebBrowser2Interface;
            IOleCommandTargetInterface = (IOleCommandTarget)IWebBrowser2Interface;
            // Set client site
            IOleObjectInterface.SetClientSite(this);

            IWebBrowser2Interface.RegisterAsDropTarget = true;
            IWebBrowser2Interface.Silent = true;

            if (IConnectionPointInterface == null)
            {
                // Get connectionpointcontainer
                IConnectionPointContainer cpCont = (IConnectionPointContainer)IWebBrowser2Interface;
                //Find connection point
                Guid guid = typeof(DWebBrowserEvents2).GUID;
                IConnectionPointInterface = null;
                cpCont.FindConnectionPoint(ref guid, out IConnectionPointInterface);
                // Advice
                IConnectionPointInterface.Advise(this, out dwCookie);
            }
        }

        /// <summary>
        /// Executes the specified command in MSHTML with the specified argument
        /// </summary>
        /// <param name="command"></param>
        protected internal void OleCommandExec(int command, object argument)
        {
            object[] args = new object[] { argument };

            if (IOleCommandTargetInterface != null)
            {
                //Execute the command
                int hr = IOleCommandTargetInterface.Exec(ref Iid_Clsids.Guid_MSHTML, (uint)command, (uint)OLECMDEXECOPT.OLECMDEXECOPT_DONTPROMPTUSER, args, null);
            }
        }

        public string GetText(bool asHtml)
        {
            if (this.mshtml == null)
                return string.Empty;
            IHTMLDocument3 doc3 = null;
            doc3 = mshtml.Document as IHTMLDocument3;
            if (doc3 != null)
            {
                if (asHtml)
                {
                    if (doc3.documentElement == null)
                        return string.Empty;
                    if (doc3.documentElement.outerHTML == null)
                        return string.Empty;
                    return doc3.documentElement.outerHTML;
                }
                else
                {
                    if (doc3.documentElement.outerText == null)
                        return string.Empty;
                    return doc3.documentElement.outerText;
                }
            }
            return string.Empty;
        }
        public string GetSelectedText(bool asHtml)
        {
            if (this.mshtml == null)
                return string.Empty;

            IHTMLDocument2 doc2 = null;
            IHTMLSelectionObject selobj = null;
            IHTMLTxtRange range = null;

            doc2 = mshtml.Document as IHTMLDocument2;
            if ((doc2 == null) || (doc2.selection == null))
                return string.Empty;

            selobj = doc2.selection as IHTMLSelectionObject;
            if (selobj == null)
                return string.Empty;

            if ((selobj.EventType == "none") || (selobj.EventType == "control"))
                return string.Empty;

            range = selobj.createRange() as IHTMLTxtRange;
            if (range == null)
                return string.Empty;

            if (asHtml)
                return range.htmlText;
            else
                return range.text;
        }

        public void InsertHtml(string html)
        {
            if (this.mshtml == null)
                return;

            IHTMLDocument2 doc2 = null;
            IHTMLSelectionObject selobj = null;
            IHTMLTxtRange range = null;

            doc2 = mshtml.Document as IHTMLDocument2;
            if ((doc2 == null) || (doc2.selection == null))
                return;

            selobj = doc2.selection as IHTMLSelectionObject;
            if (selobj == null)
                return;

            range = selobj.createRange() as IHTMLTxtRange;
            if (range == null)
                return;

            range.pasteHTML(html);
            this.OnHtmlChanged();
        }

        /// <summary>
        /// Disposes the current document.
        /// </summary>
        private void DisposeDocument()
        {
            lock (this._syncRoot)
            {
                this.mshtml.LoadCompleted -= new LoadCompletedEventHandler(webBrowser_LoadCompleted);
                if ((IConnectionPointInterface != null) && (dwCookie > 0))
                    IConnectionPointInterface.Unadvise(dwCookie);
                IConnectionPointInterface = null;
            }
        }

        public bool SetDesignMode(bool mode)
        {
            if (this.mshtml.Document == null)
                return false;

            IHTMLDocument2 doc2 = this.mshtml.Document as IHTMLDocument2;
            if (doc2 != null)
            {
                doc2.designMode = mode ? "on" : "off";
                return true;
            }
            return false;
        }
        public bool GetDesignMode()
        {
            if (this.mshtml.Document == null)
                return false;

            IHTMLDocument2 doc2 = this.mshtml.Document as IHTMLDocument2;
            if (doc2 != null)
                return doc2.designMode.CompareTo("on") == 0;
            return false;
        }
        public bool IsCommandEnabled(string sCmdId)
        {
            if (this.mshtml == null)
                return false;
            IHTMLDocument2 doc2 = this.mshtml.Document as IHTMLDocument2;
            if (doc2 != null)
                return doc2.queryCommandEnabled(sCmdId);
            return false;
        }

        public void SetFocus()
        {
            IHTMLDocument4 doc4 = this.mshtml.Document as IHTMLDocument4;
            if (doc4 != null)
            {
                doc4.focus();
            }
        }

        void webBrowser_LoadCompleted(object sender, NavigationEventArgs e)
        {
            if (!this.IsDocumentLoaded)
            {
                try
                {
                    TopLevelHtmlDocumentevents.DisconnectHtmlEvents();
                    IHTMLDocument2 doc2 = mshtml.Document as IHTMLDocument2;
                    if (doc2 != null)
                    {
                        TopLevelHtmlDocumentevents.ConnectToHtmlEvents(doc2);
                    }
                    SetDesignMode(true);
                    SetFocus();
                }
                finally
                {
                    this.IsDocumentLoaded = true;
                }
            }
        }

        /// <summary>
        /// Executes the specified command against the html document.
        /// </summary>
        /// <param name="command"></param>
        public void ExecuteCommand(string command)
        {
            this.ExecuteCommand(command, null);
        }

        /// <summary>
        /// Executes the specified command against the html document, with the specified argument.
        /// </summary>
        /// <param name="command"></param>
        /// <param name="value"></param>
        public void ExecuteCommand(string command, object value)
        {
            if (!this.IsDocumentLoaded) return;

            //execute the command
            if (this.mshtml == null)
                return;
            IHTMLDocument2 doc2 = this.mshtml.Document as IHTMLDocument2;
            if (doc2 != null)
            {
                doc2.execCommand(command, true, value);
                //raise the html changed event
                this.OnHtmlChanged();
            }
        }

        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            if (!System.ComponentModel.DesignerProperties.GetIsInDesignMode(this))
            {
            }
        }

        #region Interfaces Implementation

        #region IOleClientSite Members

        int IOleClientSite.SaveObject()
        {
            return Hresults.E_NOTIMPL;
            //throw new Exception("The method or operation is not implemented.");
        }

        int IOleClientSite.GetMoniker(uint dwAssign, uint dwWhichMoniker, out IMoniker ppmk)
        {
            ppmk = null;
            return Hresults.E_NOTIMPL;
        }

        int IOleClientSite.GetContainer(out IOleContainer ppContainer)
        {
            ppContainer = null;
            return Hresults.E_NOTIMPL;
        }

        int IOleClientSite.ShowObject()
        {
            return Hresults.S_OK;
        }

        int IOleClientSite.OnShowWindow(bool fShow)
        {
            return Hresults.E_NOTIMPL;
        }

        int IOleClientSite.RequestNewObjectLayout()
        {
            return Hresults.E_NOTIMPL;
        }

        #endregion

        #region IDocHostShowUI Members
        //Can not catch VBScript MsgBox and InputBox functions and javascript prompt
        //This does catch alert and confirm (VBScript as well)
        int IDocHostShowUI.ShowMessage(IntPtr hwnd, string lpstrText,
            string lpstrCaption, uint dwType,
            string lpstrHelpFile, uint dwHelpContext, ref int lpResult)
        {
            int iRet = Hresults.S_FALSE;

            return iRet;
        }

        int IDocHostShowUI.ShowHelp(IntPtr hwnd, string pszHelpFile, uint uCommand, uint dwData, tagPOINT ptMouse, object pDispatchObjectHit)
        {
            return Hresults.E_NOTIMPL;
        }

        #endregion

        #region IDocHostUIHandler Members

        int IDocHostUIHandler.ShowContextMenu(uint dwID, ref tagPOINT pt, object pcmdtReserved, object pdispReserved)
        {
            //    return Hresults.S_OK; //Do not display context menu
            //    return Hresults.S_FALSE; //Default IE ctxmnu

            Point outpt = new Point(pt.X, pt.Y);
            //Raise event
            //if (WBContextMenu != null)
            //{
            //    ContextMenuEvent.SetParameters((WB_CONTEXTMENU_TYPES)dwID, outpt, pdispReserved);
            //    WBContextMenu(this, ContextMenuEvent);
            //    if (ContextMenuEvent.displaydefault == false) //Handled or don't display
            //        return Hresults.S_OK;
            //    ContextMenuEvent.dispctxmenuobj = null;
            //}
            return Hresults.S_FALSE;
        }

        int IDocHostUIHandler.GetHostInfo(ref DOCHOSTUIINFO info)
        {
            info.cbSize = (uint)Marshal.SizeOf(info);
            info.dwDoubleClick = (uint)0;
            info.dwFlags = (uint)(DOCHOSTUIFLAG.DISABLE_HELP_MENU |
                DOCHOSTUIFLAG.NO3DBORDER | DOCHOSTUIFLAG.OPENNEWWIN |
                DOCHOSTUIFLAG.ACTIVATE_CLIENTHIT_ONLY | DOCHOSTUIFLAG.THEME);
            info.pchHostCss = "BODY {background-color:#ffffff}";
            return Hresults.S_OK;
        }

        int IDocHostUIHandler.ShowUI(int dwID, IOleInPlaceActiveObject activeObject, IOleCommandTarget commandTarget, IOleInPlaceFrame frame, IOleInPlaceUIWindow doc)
        {
            return Hresults.S_OK;
        }

        int IDocHostUIHandler.HideUI()
        {
            return Hresults.S_OK;
        }

        int IDocHostUIHandler.UpdateUI()
        {
            return Hresults.S_OK;
        }

        int IDocHostUIHandler.EnableModeless(bool fEnable)
        {
            return Hresults.E_NOTIMPL;
        }

        int IDocHostUIHandler.OnDocWindowActivate(bool fActivate)
        {
            return Hresults.E_NOTIMPL;
        }

        int IDocHostUIHandler.OnFrameWindowActivate(bool fActivate)
        {
            return Hresults.E_NOTIMPL;
        }

        int IDocHostUIHandler.ResizeBorder(ref tagRECT rect, IOleInPlaceUIWindow doc, bool fFrameWindow)
        {
            return Hresults.E_NOTIMPL;
        }

        int IDocHostUIHandler.TranslateAccelerator(ref tagMSG msg, ref Guid group, uint nCmdID)
        {
            switch (nCmdID)
            {
                case (uint)MSHTML_COMMAND_IDS.IDM_PASTE:
                case (uint)MSHTML_COMMAND_IDS.IDM_PASTEINSERT:
                    {
                        RoutedEventArgs args = new RoutedEventArgs(BeforePasteEvent);
                        this.RaiseEvent(args);
                        if (args.Handled == true)
                        {
                            return Hresults.S_OK;
                        }
                    }
                    break;
                // block commands that are not suited for an editor
                case (uint)MSHTML_COMMAND_IDS.IDM_CODE:
                case (uint)MSHTML_COMMAND_IDS.IDM_FIND:
                case (uint)MSHTML_COMMAND_IDS.IDM_OBJECTVERBLIST0:
                case (uint)MSHTML_COMMAND_IDS.IDM_OBJECTVERBLIST1:
                case (uint)MSHTML_COMMAND_IDS.IDM_OBJECTVERBLIST2:
                case (uint)MSHTML_COMMAND_IDS.IDM_OBJECTVERBLIST3:
                case (uint)MSHTML_COMMAND_IDS.IDM_OBJECTVERBLIST4:
                case (uint)MSHTML_COMMAND_IDS.IDM_OBJECTVERBLIST5:
                case (uint)MSHTML_COMMAND_IDS.IDM_OBJECTVERBLIST6:
                case (uint)MSHTML_COMMAND_IDS.IDM_OBJECTVERBLIST7:
                case (uint)MSHTML_COMMAND_IDS.IDM_OBJECTVERBLIST8:
                case (uint)MSHTML_COMMAND_IDS.IDM_OBJECTVERBLIST9:
                case (uint)MSHTML_COMMAND_IDS.IDM_RENAME:
                case (uint)MSHTML_COMMAND_IDS.IDM_IMPORT:
                case (uint)MSHTML_COMMAND_IDS.IDM_NEWPAGE:
                case (uint)MSHTML_COMMAND_IDS.IDM_FOLLOW_ANCHOR:
                case (uint)MSHTML_COMMAND_IDS.IDM_EDITSOURCE:
                case (uint)MSHTML_COMMAND_IDS.IDM_BOOKMARK:
                case (uint)MSHTML_COMMAND_IDS.IDM_HYPERLINK:
                case (uint)MSHTML_COMMAND_IDS.IDM_BROWSEMODE:
                case (uint)MSHTML_COMMAND_IDS.IDM_EDITMODE:
                case (uint)MSHTML_COMMAND_IDS.IDM_UNBOOKMARK:
                case (uint)MSHTML_COMMAND_IDS.IDM_TOOLBARS:
                case (uint)MSHTML_COMMAND_IDS.IDM_STATUSBAR:
                case (uint)MSHTML_COMMAND_IDS.IDM_VIEWSOURCE:
                case (uint)MSHTML_COMMAND_IDS.IDM_HTMLSOURCE:
                case (uint)MSHTML_COMMAND_IDS.IDM_PRINTQUERYJOBSPENDING:
                case (uint)MSHTML_COMMAND_IDS.IDM_GOBACKWARD:
                case (uint)MSHTML_COMMAND_IDS.IDM_GOFORWARD:
                case (uint)MSHTML_COMMAND_IDS.IDM_PRESTOP:
                case (uint)MSHTML_COMMAND_IDS.IDM_MP_EMAILPICTURE:
                case (uint)MSHTML_COMMAND_IDS.IDM_MP_PRINTPICTURE:
                case (uint)MSHTML_COMMAND_IDS.IDM_STOPDOWNLOAD:
                case (uint)MSHTML_COMMAND_IDS.IDM_HTMLEDITMODE:
                case (uint)MSHTML_COMMAND_IDS.IDM_OPEN:
                case (uint)MSHTML_COMMAND_IDS.IDM_NEW:
                case (uint)MSHTML_COMMAND_IDS.IDM_SAVE:
                case (uint)MSHTML_COMMAND_IDS.IDM_SAVEAS:
                case (uint)MSHTML_COMMAND_IDS.IDM_SAVECOPYAS:
                case (uint)MSHTML_COMMAND_IDS.IDM_PRINTPREVIEW:
                case (uint)MSHTML_COMMAND_IDS.IDM_SHOWPRINT:
                case (uint)MSHTML_COMMAND_IDS.IDM_SHOWPAGESETUP:
                case (uint)MSHTML_COMMAND_IDS.IDM_PRINT:
                case (uint)MSHTML_COMMAND_IDS.IDM_PAGESETUP:
                case (uint)MSHTML_COMMAND_IDS.IDM_ZOOMPERCENT:
                case (uint)MSHTML_COMMAND_IDS.IDM_GETZOOM:
                case (uint)MSHTML_COMMAND_IDS.IDM_STOP:
                    return Hresults.S_OK;

            }

            //IE default action
            return Hresults.S_FALSE;
        }

        int IDocHostUIHandler.GetOptionKeyPath(out string pbstrKey, uint dw)
        {
            pbstrKey = null;
            return Hresults.E_NOTIMPL;
        }

        int IDocHostUIHandler.GetDropTarget(IfacesEnumsStructsClasses.IDropTarget pDropTarget, out IfacesEnumsStructsClasses.IDropTarget ppDropTarget)
        {
            int hret = Hresults.E_NOTIMPL;
            ppDropTarget = null;
            ppDropTarget = this as IfacesEnumsStructsClasses.IDropTarget;
            if (ppDropTarget != null)
                hret = Hresults.S_OK;
            OriginalDropTarget = pDropTarget;
            return hret;
        }

        int IDocHostUIHandler.GetExternal(out object ppDispatch)
        {
            ppDispatch = null;
            return Hresults.E_NOTIMPL;
        }

        int IDocHostUIHandler.TranslateUrl(uint dwTranslate, string strURLIn, out string pstrURLOut)
        {
            pstrURLOut = null;
            return Hresults.E_NOTIMPL;
        }

        int IDocHostUIHandler.FilterDataObject(System.Runtime.InteropServices.ComTypes.IDataObject pDO, out System.Runtime.InteropServices.ComTypes.IDataObject ppDORet)
        {
            ppDORet = null;
            return Hresults.E_NOTIMPL;
        }

        #endregion

        #region DWebBrowserEvents2 Members

        [System.Runtime.InteropServices.DispId(250)]
        void DWebBrowserEvents2.BeforeNavigate2(object pDisp, ref object URL, ref object Flags, ref object TargetFrameName, ref object PostData, ref object Headers, ref bool Cancel)
        {
            string url = URL as string;
            BeforeNavigate2EventArgs args = new BeforeNavigate2EventArgs(BeforeNavigate2Event, url);
            this.RaiseEvent(args);
            if (args.Handled == true)
            {
                Cancel = true;
            }
        }

        [DispId(268)]
        void DWebBrowserEvents2.ClientToHostWindow(ref int CX, ref int CY)
        {
        }

        [DispId(105)]
        void DWebBrowserEvents2.CommandStateChange(int Command, bool Enable)
        {
        }

        [DispId(104)]
        void DWebBrowserEvents2.DocumentComplete(object pDisp, ref object URL)
        {
        }

        [DispId(106)]
        void DWebBrowserEvents2.DownloadBegin()
        {
        }

        [DispId(259)]
        void DWebBrowserEvents2.DownloadComplete()
        {
        }

        [DispId(270)]
        void DWebBrowserEvents2.FileDownload(bool ActiveDocument, ref bool Cancel)
        {
        }

        [DispId(252)]
        void DWebBrowserEvents2.NavigateComplete2(object pDisp, ref object URL)
        {
        }

        [DispId(271)]
        void DWebBrowserEvents2.NavigateError(object pDisp, ref object URL, ref object Frame, ref object StatusCode, ref bool Cancel)
        {
        }

        [DispId(251)]
        void DWebBrowserEvents2.NewWindow2(ref object ppDisp, ref bool Cancel)
        {
        }

        [DispId(0x111)]
        void DWebBrowserEvents2.NewWindow3(ref object ppDisp, ref bool Cancel, uint dwFlags, string bstrUrlContext, string bstrUrl)
        {
        }

        #region Unused Events
        void DWebBrowserEvents2.OnFullScreen(bool FullScreen)
        {
        }

        void DWebBrowserEvents2.OnMenuBar(bool MenuBar)
        {
        }

        void DWebBrowserEvents2.OnQuit()
        {
        }

        void DWebBrowserEvents2.OnStatusBar(bool StatusBar)
        {
        }

        void DWebBrowserEvents2.OnTheaterMode(bool TheaterMode)
        {
        }

        void DWebBrowserEvents2.OnToolBar(bool ToolBar)
        {
        }

        void DWebBrowserEvents2.OnVisible(bool Visible)
        {
        }
        #endregion

        [DispId(227)]
        void DWebBrowserEvents2.UpdatePageStatus(object pDisp, ref object nPage, ref object fDone)
        {
        }

        [DispId(225)]
        void DWebBrowserEvents2.PrintTemplateInstantiation(object pDisp)
        {
        }

        [DispId(226)]
        void DWebBrowserEvents2.PrintTemplateTeardown(object pDisp)
        {
        }

        [DispId(272)]
        void DWebBrowserEvents2.PrivacyImpactedStateChange(bool bImpacted)
        {
        }

        [DispId(108)]
        void DWebBrowserEvents2.ProgressChange(int Progress, int ProgressMax)
        {
        }

        [DispId(112)]
        void DWebBrowserEvents2.PropertyChange(string szProperty)
        {
        }

        [DispId(269)]
        void DWebBrowserEvents2.SetSecureLockIcon(int SecureLockIcon)
        {
        }

        [DispId(102)]
        void DWebBrowserEvents2.StatusTextChange(string Text)
        {
        }

        [DispId(113)]
        void DWebBrowserEvents2.TitleChange(string Text)
        {
        }

        //DWebBrowserEvents2.WindowClosing is never called?
        //So here is the workaround
        [DispId(263)]
        public void DISPATCH_WindowClosing(bool IsChildWindow, ref bool Cancel)
        {
        }

        [DispId(263)]
        void DWebBrowserEvents2.WindowClosing(bool IsChildWindow, ref bool Cancel)
        {
        }

        [DispId(267)]
        void DWebBrowserEvents2.WindowSetHeight(int Height)
        {
        }

        [DispId(264)]
        void DWebBrowserEvents2.WindowSetLeft(int Left)
        {
        }

        [DispId(262)]
        void DWebBrowserEvents2.WindowSetResizable(bool Resizable)
        {
        }

        [DispId(265)]
        void DWebBrowserEvents2.WindowSetTop(int Top)
        {
        }

        [DispId(266)]
        void DWebBrowserEvents2.WindowSetWidth(int Width)
        {
        }

        #endregion

        #region IDropTarget Members

        int IfacesEnumsStructsClasses.IDropTarget.DragEnter(System.Runtime.InteropServices.ComTypes.IDataObject pDataObj, uint grfKeyState, tagPOINT pt, ref uint pdwEffect)
        {
            DataObject obja = null;
            if (pDataObj != null)
                obja = new DataObject(pDataObj);
            Point ppt = new Point(pt.X, pt.Y);
            DragEnterEventArgs args = new DragEnterEventArgs(DragEnterEvent, obja, grfKeyState, ppt, pdwEffect);
            this.RaiseEvent(args);
            if (args.Handled == true)
            {
                pdwEffect = (uint)args.dropeffect;
            }
            else
            {
                if (OriginalDropTarget != null)
                {
                    return OriginalDropTarget.DragEnter(pDataObj, grfKeyState, pt, ref pdwEffect);
                }
            }
            return Hresults.S_OK;
        }
        int IfacesEnumsStructsClasses.IDropTarget.DragOver(uint grfKeyState, tagPOINT pt, ref uint pdwEffect)
        {
            Point ppt = new Point(pt.X, pt.Y);
            DragOverEventArgs args = new DragOverEventArgs(DragOverEvent, grfKeyState, ppt, pdwEffect);
            this.RaiseEvent(args);
            if (args.Handled == true)
            {
                pdwEffect = (uint)args.dropeffect;
            }
            else
            {
                if (OriginalDropTarget != null)
                {
                    return OriginalDropTarget.DragOver(grfKeyState, pt, ref pdwEffect);
                }
            }
            return Hresults.S_OK;
        }
        int IfacesEnumsStructsClasses.IDropTarget.DragLeave()
        {
            RoutedEventArgs args = new RoutedEventArgs(DragLeaveEvent);
            this.RaiseEvent(args);
            if (args.Handled == false)
            {
                if (OriginalDropTarget != null)
                {
                    return OriginalDropTarget.DragLeave();
                }
            }
            return Hresults.S_OK;
        }
        int IfacesEnumsStructsClasses.IDropTarget.Drop(System.Runtime.InteropServices.ComTypes.IDataObject pDataObj, uint grfKeyState, tagPOINT pt, ref uint pdwEffect)
        {
            if (pDataObj == null)
                return Hresults.S_OK;

            DataObject obja = new DataObject(pDataObj);
            Point ppt = new Point(pt.X, pt.Y);
            DropEventArgs args = new DropEventArgs(DropEvent, grfKeyState, ppt, obja, pdwEffect);
            this.RaiseEvent(args);

            if (args.Handled == true)
            {
                pdwEffect = (uint)args.dropeffect;
            }
            else
            {
                if (OriginalDropTarget != null)
                {
                    return OriginalDropTarget.Drop(pDataObj, grfKeyState, pt, ref pdwEffect);
                }
            }
            return Hresults.S_OK;
        }

        #endregion

        #region IOleCommandTarget Members

        int IOleCommandTarget.QueryStatus(IntPtr pguidCmdGroup, uint cCmds, ref tagOLECMD prgCmds, IntPtr pCmdText)
        {
            return Hresults.OLECMDERR_E_NOTSUPPORTED;
        }

        //pguidCmdGroup must be declared as IntPtr not a GUID, as pguidCmdGroup may be null
        int IOleCommandTarget.Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, object[] pvaIn, object[] pvaOut)
        {
            int hr = Hresults.OLECMDERR_E_NOTSUPPORTED;

            return hr;
        }

        #endregion

        #region IServiceProvider Members

        int IfacesEnumsStructsClasses.IServiceProvider.QueryService(ref Guid guidService, ref Guid riid, out IntPtr ppvObject)
        {
            int hr = Hresults.E_NOINTERFACE;
            ppvObject = IntPtr.Zero;

            if (riid == Iid_Clsids.IID_IProtectFocus) //IE7 + Vista
            {
                ppvObject = Marshal.GetComInterfaceForObject(this, typeof(IProtectFocus));
                hr = Hresults.S_OK;
            }
            else if (riid == Iid_Clsids.IID_IDropTarget)
            {
                ppvObject = Marshal.GetComInterfaceForObject(this, typeof(IDropTarget));
                hr = Hresults.S_OK;
            }
            else if (riid == Iid_Clsids.IID_IDocHostShowUI)
            {
                ppvObject = Marshal.GetComInterfaceForObject(this, typeof(IDocHostShowUI));
                hr = Hresults.S_OK;
            }

            return hr;
        }

        #endregion

        #region IProtectFocus Members

        void IProtectFocus.AllowFocusChange(ref bool pfAllow)
        {
            pfAllow = false;
        }

        #endregion

        #endregion //Interfaces Implementation


        #region IHTMLEventCallBack Members

        public bool HandleHTMLEvent(HTMLEventType EventType, HTMLEventDispIds EventDispId, IHTMLEventObj pEvtObj)
        {
            bool handled = false;
            if (EventType == HTMLEventType.HTMLDocumentEvent)
            {
                switch (EventDispId)
                {
                    case HTMLEventDispIds.ID_ONMOUSEOVER:
                        {
                            // handling an anchor tag hover, I know there is probably better way to handle this.
                            if (pEvtObj.SrcElement.tagName.ToLowerInvariant() == "a")
                            {
                                IHTMLDocument2 doc2 = mshtml.Document as IHTMLDocument2;
                                if (doc2 != null)
                                {
                                    string href = pEvtObj.SrcElement.getAttribute("href", 0) as string;
                                    if (href != null)
                                    {
                                        StringBuilder sb = new StringBuilder(51);
                                        WinApis.PathCompactPathEx(sb, href, 51, 0);
                                        href = sb.ToString();
                                    }

                                    string innerHtml = string.Format("<P>&nbsp{0}...<BR>&nbsp<B>{1}</B></P>", href, "Ctrl+click to follow");

                                    HtmlPopup.ShowPopup(doc2, innerHtml, pEvtObj.ClientX, pEvtObj.ClientY, 300, 30);
                                }
                            }
                        }
                        break;
                    case HTMLEventDispIds.ID_ONMOUSEOUT:
                        {
                            //are we on an anchor tag.
                            if (pEvtObj.SrcElement.tagName.ToLowerInvariant() == "a")
                            {
                                HtmlPopup.HidePopup();
                            }
                        }
                        break;
                    case HTMLEventDispIds.ID_ONCLICK:
                    //case HTMLEventDispIds.ID_ONMOUSEDOWN:
                        {
                            if (Keyboard.Modifiers == ModifierKeys.Control)
                            {
                                if (pEvtObj.SrcElement.tagName.ToLowerInvariant() == "a")
                                {
                                    IHTMLAnchorElement anchor = pEvtObj.SrcElement as IHTMLAnchorElement;
                                    if (anchor != null)
                                    {
                                        handled = true;
                                        System.Diagnostics.Process.Start(anchor.href);
                                    }
                                }
                            }
                        }
                        break;
                    case HTMLEventDispIds.ID_ONDBLCLICK:
                        {
                            if (pEvtObj.SrcElement.tagName.ToLowerInvariant() == "a")
                            {
                                IHTMLAnchorElement anchor = pEvtObj.SrcElement as IHTMLAnchorElement;
                                if (anchor != null)
                                {
                                    handled = true;
                                    System.Diagnostics.Process.Start(anchor.href);
                                }
                            }
                        }
                        break;
                    default:
                        break;
                }
            }
            return handled;
        }

        #endregion
    }
}
